package com.lsl.rdf.exception;

import com.fasterxml.jackson.core.JsonParseException;
import com.lsl.rdf.BaseException;
import com.lsl.rdf.UnauthorizedException;
import com.lsl.rdf.errCode.ErrorCode;
import com.lsl.rdf.result.BaseResult;
import com.lsl.rdf.result.error.ErrorResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;

/**
 * Created by lsl on 2021/6/9.
 */
@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler extends BaseResult<Object> implements ResponseBodyAdvice<Object> {

    @Value("${spring.application.name:unknown-application}")
    private String application;

    @Value("${server.port:8080}")
    private String port;

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass,
                                  ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (body instanceof ErrorResult) {
            String url = serverHttpRequest.getURI().getPath();
            ErrorResult result = (ErrorResult) body;
            result.setApplication(application);
            result.setUrl("http://" + getAddress() + ":" + port + url);
            return result;
        }
        return body;
    }

    /**
     * 业务异常处理器
     */
    @ExceptionHandler(value = BaseException.class)
    @ResponseStatus(value = HttpStatus.OK)
    public BaseResult<?> baseExceptionHandler(BaseException e) {
        log.error("Base exception stack trace: ", e);
        return error(e.getMessage());
    }

    /**
     * 其他未知异常处理器
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(value = HttpStatus.OK)
    public BaseResult<?> unknownExceptionHandler(Exception e) {
        Throwable cause = e.getCause();
        log.error("Unknown exception stack trace: ", e);
        return error(ErrorCode.UNKNOWN_SERVER_ERROR, Objects.nonNull(cause) ? cause.getMessage() : e.getMessage());
    }

    /**
     * 授权异常
     */
    @ExceptionHandler(value = UnauthorizedException.class)
    @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
    public BaseResult<?> unauthorizedExceptionHandler(UnauthorizedException e) {
        return error(ErrorCode.PERMISSION_DENIED, e.getMessage());
    }

    /**
     * 错误请求异常处理器
     */
    @ExceptionHandler(value = {
            BindException.class,
            MethodArgumentNotValidException.class,
            MethodArgumentTypeMismatchException.class,
            MissingServletRequestParameterException.class,
            HttpRequestMethodNotSupportedException.class,
            JsonParseException.class,
            HttpMessageNotReadableException.class,
            HttpMediaTypeNotSupportedException.class
    })
    @ResponseStatus(value = HttpStatus.OK)
    public BaseResult<?> badRequestExceptionHandler(Exception e) {
        if (e instanceof MethodArgumentNotValidException) {
            return error400(((MethodArgumentNotValidException) e).getBindingResult().getAllErrors().get(0).getDefaultMessage(), e.getMessage());
        } else if (e instanceof BindException) {
            return error400(((BindException) e).getAllErrors().get(0).getDefaultMessage(), e.getMessage());
        } else {
            return error400(e.getMessage());
        }
    }


    /**
     * 获取主机地址
     */
    private String getAddress() {
        String hostAddress = null;
        try {
            hostAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("ExceptionHandler.getAddress 未知主机", e);
        }
        if (hostAddress != null) {
            String[] ipArr = hostAddress.split("\\.");
            hostAddress = "*.*." + ipArr[2] + "." + ipArr[3];
        }
        return hostAddress;
    }
}
